package org.andengine.shmup;

import java.util.Random;

import org.simpleframework.xml.*;

import team2.game.crow.SpritePool;

@Root (name = "wave")
public class EnemyWave {
	
	/*
	 * Adjusts how fast enemies fly
	 * 0 = slow down ship
	 * 1 = normal speed
	 * 2 = increase speed
	 * 3 = faster ship
	 */
	@Element (required = false)
	private short speed_level = 1;
	
	@Element
	private String type;
	
	@Element
	private int quantity;

	@Element
	private String origin;
	
	@Element (required = false)
	private String trajectory = "LINEAR"; //i.e. typical flight path.
											/*
											 *  We have
											 *  LINEAR - straight vertical line.
											 *  SKEIN - V formation
											 *  ECHELON - slanted line
											 *  B-WAVE - a special one used for Bladers
											 *  TRIANGLE - 
											 */
	
	@Element (required = false)
	private String bWaveStart = "CENTER";
	
	@Element (required = false)
	private String waveDir = "RIGHT";
	
	@Element (required = false)
	private float waveWidth = 0; //akin to amplitude
	
	@Element (required = false)
	private float waveDensity = 0; //how many enemies are in between the peaks.
	
	@Element (required = false)
	private float radius;
	
	@Element (required = false)
	private float spacing = 100;
	
	@Element (required = false)
	private float angle = 45;
	
	@Element (required = false)
	private float horizOff = 1; // percentage based
	
	@Element (required = false, name = "randomized")
	private String randomizedSpawn = "NO"; 
		
	@Element (required = false)
	private float px_offset;
	
	@Element (required = false)
	private float py_offset;
	
	@Element (required = false)
	public String spawnType = "QUEUE"; //Two ways: Queue and Delay. 
										//Queue is spawn immediately after next wave is killed.
									    //Delay is wait until all active waves are inactive,
	
	private Enemy[] wave;
	private Random rng = new Random();
	
	public EnemyWave(){
		setQuantity(4);
		setOrigin("NORTH");
		setTrajectory("LINEAR");
		setEnemyType("GRUNT");
	}
	
	public EnemyWave(int quant, String type, String path, String dir){
		setQuantity(quant);
		setEnemyType(type.toUpperCase());
		
		//paranoia sets in.
		dir = dir.toUpperCase();
		if(!(dir.equals("NORTH")||
				dir.equals("NORTHEAST")||
				dir.equals("NORTHWEST")||
				dir.equals("EAST")||
				dir.equals("WEST")||
				dir.equals("SOUTH")||
				dir.equals("SOUTHEAST")||
				dir.equals("SOUTHWEST"))){
			dir = "NORTH";
		}
		setOrigin(dir);
		setTrajectory(path);
	}
	
	public EnemyWave(int quant, String type, String path, String dir, float x, float y){
		this(quant, type, path, dir);
		this.setXPosMod(x);
		this.setYPosMod(y);
	}
	
	public void loadBullets(SpritePool pool){
		pool.batchAllocatePoolItems(15 * this.quantity);
	}

	/*
	 * The main purpose of the Enemy Wave class.
	 * The whole entire thing to both store XML-parsed data
	 * and generate enemies based on that data.
	 * 
	 * It might be necessary to have a 2-dimensional structure
	 * to store each wave, overwriting spaces as the game goes on.
	 * 
	 * At most, I propose that 3 waves are loaded onto a scene at
	 * a time, 2 as active enemies, the third as the queued wave.
	 */
	public Enemy[] spawnEnemies(EnemyPool ep){
		int i = 0;
		int rotation = 0;
		float rx = 0.0f, dx = 0.0f; //rx = some kind of position offset.
		float px = ep.getCameraWidth()/2;
		float py = 0.0f;
		float h = horizOff;
		
		wave = new Enemy[this.quantity];
		boolean isV = this.getsetTrajectory().equals("SKEIN");
		boolean isBlader = this.getEnemyType().equals("BLADER");
		boolean isBWave = this.getsetTrajectory().equals("B-WAVE");
		boolean isSlant = this.getsetTrajectory().equals("ECHELON");
		
		
		
		while(i < quantity){
			rotation = 0;
			
			if(randomizedSpawn.equals("YES"))
				h = Math.abs(horizOff + (rng.nextFloat() - 0.5f)*(rng.nextFloat()*.05f));
			
			/*
			if(origin.contains("EAST") && !isBlader){
			
				px = 11*(ep.getCameraWidth()/10.0f) + rx;
				py = ep.getCameraHeight()/2;
				if(isV){
					py += spacing * (int)(i+1)/2 * (float) Math.pow(-1, i);
				}
				rotation += -90;
			}
			else if(origin.contains("WEST") && !isBlader){
				px = -(ep.getCameraWidth()/10.0f) - rx;
				py = ep.getCameraHeight()/2;
				if(isV){
					py += spacing * (int)(i+1)/2 * (float) Math.pow(-1, i);
				}
				rotation += 90;
			}
			*/
			if(origin.contains("NORTH") || origin.contains("SOUTH")){
				if(origin.contains("EAST")) px = 7*(ep.getCameraWidth()/10.0f);
				else if(origin.contains("WEST")) px = 3*(ep.getCameraWidth()/10.0f);
				else px = (ep.getCameraWidth()/2);
				
				py = (ep.getCameraHeight()/10.0f + rx *(float)Math.cos(Math.toRadians(angle)));
				
				if(origin.contains("SOUTH")){
					py *= 11;
					rotation = 180;
				}
				else{
					py *= -1;
				}
				
				if(isV) {
					if(waveDir.equals("LEFT"))
						px += rx * (float) Math.pow(-1, i) *(float)Math.sin(Math.toRadians(angle-90));
					else if(waveDir.equals("RIGHT"))
						px += rx * (float) Math.pow(-1, i) *(float)Math.sin(Math.toRadians(angle));
				}
				else if(isBWave){

					if(this.bWaveStart.equals("LEFT") && i == 0){
						if(bWaveStart.equals(waveDir)) waveDir = "RIGHT";
						px -= waveWidth/2;
					}
					else if(this.bWaveStart.equals("RIGHT") && i == 0){
						if(bWaveStart.equals(waveDir)) waveDir = "LEFT";
						px += waveWidth/2;
						
					}
					
					if(bWaveStart.equals("CENTER"))
						dx = (float)Math.cos(Math.toRadians(((int)(i/(waveDensity+1))*90)%360));
					else
						dx = (float)Math.sin(Math.toRadians(((int)(i/(waveDensity+1))*90)%360));
					
					if(waveDir.equals("LEFT"))
						px -= dx*waveWidth/(waveDensity+1);
					else if(waveDir.equals("RIGHT"))
						px += dx*waveWidth/(waveDensity+1);
				}
				else if(isSlant){
					if(waveDir.equals("LEFT"))
						px += spacing * i *(float)Math.sin(Math.toRadians(angle-90)); 
					else
						px += spacing * i *(float)Math.sin(Math.toRadians(angle)); 
				}
				
				rotation = (rotation/2);
			}
			
			wave[i] = ep.obtainPoolItem(trajectory, px*h, h*py, rotation);
			wave[i].setType(type);
			wave[i].setName(type + i);
			wave[i].setInitRotation(rotation);
			
			if(isBlader){
				wave[i].setVelocity(0, wave[i].getVelocity().y*3f);
			}
			wave[i].setVelocity(0, wave[i].getVelocity().y*(1+(speed_level*.5f)));
			
			/*if(this.getsetTrajectory().equals("LINEAR_SPREAD")){
				rotation += 5 * (int)((i+1)/2) * (float) Math.pow(-1, i);
			}*/
			if(isV){
				rx = (spacing + (float)Math.sqrt(ep.getEnemyHeight()*ep.getCameraWidth())) * (int)(i+1)/2 ;
			}
			else if(isSlant){
				rx = (spacing + (float)Math.sqrt(ep.getEnemyHeight()*ep.getCameraWidth())) * i *(float)Math.cos(Math.toRadians(angle));
			}
			else{
				rx = (spacing + (float)Math.sqrt(ep.getEnemyHeight()*ep.getCameraWidth())) * i;
			}
			
			i++;
		}
		return wave;
	}

	public void setEnemyType(String enemyType) {
		this.type = enemyType;
	}

	public String getEnemyType() {
		return type;
	}

	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}

	public int getQuantity() {
		return quantity;
	}

	public void setTrajectory(String formation) {
		this.trajectory = formation;
	}

	public String getsetTrajectory() {
		return trajectory;
	}

	public void setOrigin(String origin) {
		this.origin = origin;
	}

	public String getOrigin() {
		return origin;
	}
	
	public void setSpeedLevel(short speed_level) {
		this.speed_level = speed_level;
	}

	public short getSpeedLevel() {
		return speed_level;
	}

	public void setXPosMod(float px_offset) {
		this.px_offset = px_offset;
	}

	public float getXPosMod() {
		return px_offset;
	}

	public void setYPosMod(float py_offset) {
		this.py_offset = py_offset;
	}

	public float getYPosMod() {
		return py_offset;
	}

	public Enemy[] getEnemies(){
		return wave;
	}
	public String toString(){
		return "Type: " + this.getEnemyType()
				+ "\nNumber: " + this.quantity
				+ "\nOrigin: " + this.origin
				+ "\nTrajectory: " + this.getsetTrajectory();
	}

	public void setRadius(float radius) {
		this.radius = radius;
	}

	public float getRadius() {
		return radius;
	}
	
	public boolean isActive(){
		for(int i = 0; i < this.getQuantity(); i++){
			if(wave[i].isAlive){
				return true;
			}
		}
		return false;
	}
}
